﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using Microsoft.SharePoint.Utilities;
using SPDevLab.WebParts.JsAppWebPart.Consts;
using SPDevLab.WebParts.JsAppWebPart.Services.Base;
using SPDevLab.WebParts.JsAppWebPart.Apps;
using Microsoft.SharePoint;
using System.Runtime.Serialization.Json;

namespace SPDevLab.WebParts.JsAppWebPart.Services
{
    public class DefaultJsAppService : JsAppServiceBase
    {
        #region contructors

        public DefaultJsAppService()
        {

        }

        #endregion

        #region methods

        public override void EnsureJsAppSiteInsfastructure(SPSite site)
        {
            var appLibrary = EnsureJsAppLibrary(site);

            EnsureJsAppLibrarySettings(appLibrary);
        }

        private void EnsureJsAppLibrarySettings(SPList appLibrary)
        {
            appLibrary.Hidden = true;
            appLibrary.NoCrawl = true;

            appLibrary.Update();
        }

        private static SPList EnsureJsAppLibrary(SPSite site)
        {
            var web = site.RootWeb;
            var listUrl = SPUrlUtility.CombineUrl(web.ServerRelativeUrl, JsAppConsts.Urls.AppLibraryUrl);

            SPList targetList = null;

            try
            {
                targetList = web.GetList(listUrl);
            }
            catch (FileNotFoundException)
            {
                var listId = web.Lists.Add(JsAppConsts.Urls.AppLibraryUrl, "Stores js based application.", SPListTemplateType.DocumentLibrary);
                targetList = web.Lists[listId];

                targetList.Title = JsAppConsts.AppLibraryTitle;
                targetList.Update();
            }

            return targetList;
        }

        public override string CreateAppHtmlContent(string content, IEnumerable<JsAppToken> tokens)
        {
            if (string.IsNullOrEmpty(content))
                return content;

            var result = content;

            if (tokens != null)
            {
                foreach (var token in tokens)
                {
                    result = result.Replace(token.Name, token.Value);
                }
            }

            return result;
        }

        public override T DeserializeFromJson<T>(string value)
        {
            var serializer = new DataContractJsonSerializer(typeof(T));

            using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(value)))
                return (T)serializer.ReadObject(ms);
        }

        public override string SerializeToJson(object value)
        {
            var serializer = new DataContractJsonSerializer(value.GetType());

            using (var ms = new MemoryStream())
            {
                serializer.WriteObject(ms, value);
                return Encoding.Default.GetString(ms.ToArray());
            }
        }

        #endregion

        public override IEnumerable<AppPackage> LoadApps(SPSite site, string siteRelativeAppFolderUrl)
        {
            var rootWeb = site.RootWeb;
            var toolpartApps = rootWeb.GetFolder(siteRelativeAppFolderUrl);

            return toolpartApps.SubFolders
                               .Cast<SPFolder>()
                               .Select(f => new AppPackage(f))
                               .ToList();
        }

        public override IEnumerable<JsAppToken> GetLinkedJsAppTokens(object obj)
        {
            var result = new List<JsAppToken>();

            if (obj is SPSite)
                result.AddRange(GetSPSiteJsAppTokens(obj as SPSite));

            if (obj is JsApplicationWebPart)
                result.AddRange(GetJsAppWebpartTokens(obj as JsApplicationWebPart));

            if (obj is JsApplicationEditorPart)
                result.AddRange(GetJsAppToolpartTokens(obj as JsApplicationEditorPart));

            if (obj is AppPackage)
                result.AddRange(GetJsAppTokens(obj as AppPackage));

            return result;
        }

        protected static IEnumerable<JsAppToken> GetJsAppTokens(AppPackage app)
        {
            var result = new List<JsAppToken>();

            result.Add(new JsAppToken
            {
                Name = JsAppBuiltInTokens.ApplicationFolderUrl,
                Value = app.ServerRelativeUrl
            });

            return result;
        }

        protected static IEnumerable<JsAppToken> GetSPSiteJsAppTokens(SPSite site)
        {
            var result = new List<JsAppToken>();

            if (site != null)
            {
                var rootWeb = site.RootWeb;

                result.Add(new JsAppToken
                {
                    Name = JsAppBuiltInTokens.SysApplicationFolderUrl,
                    Value = SPUtility.ConcatUrls(rootWeb.ServerRelativeUrl, JsAppConsts.Urls.AppLibraryUrl + "/" + JsAppConsts.Folders.SystemFolderName)
                });

                result.Add(new JsAppToken
                {
                    Name = JsAppBuiltInTokens.JsRepositoryFolderUrl,
                    Value = SPUtility.ConcatUrls(rootWeb.ServerRelativeUrl, JsAppConsts.Urls.AppLibraryUrl + "/" + JsAppConsts.Folders.CDNFolderName)
                });
            }

            return result;
        }

        protected static IEnumerable<JsAppToken> GetJsAppWebpartTokens(JsApplicationWebPart jsAppWebPart)
        {
            var result = new List<JsAppToken>();

            var jsWebPartOptionContext = jsAppWebPart.GetJsAppOptionsContextAsJson();

            result.Add(new JsAppToken
            {
                Name = JsAppBuiltInTokens.ToolpartId,
                Value = jsAppWebPart.ClientID + JsAppConsts.ClientIdPostfix.ToolpartId
            });

            result.Add(new JsAppToken
            {
                Name = JsAppBuiltInTokens.ApplicationId,
                Value = jsAppWebPart.ClientID + JsAppConsts.ClientIdPostfix.ApplicationContainerId
            });

            result.Add(new JsAppToken
            {
                Name = JsAppBuiltInTokens.AppProperties,
                Value = jsWebPartOptionContext
            });

            return result;
        }

        protected static IEnumerable<JsAppToken> GetJsAppToolpartTokens(JsApplicationEditorPart jsAppToolpart)
        {
            var result = new List<JsAppToken>();

            result.Add(new JsAppToken
            {
                Name = JsAppBuiltInTokens.AppResultId,
                Value = jsAppToolpart.AppResultValueClientId
            });

            return result;
        }
    }
}
